<?php

namespace Xrfg\Wallet\CollectERC\Services;

use Exception;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
use phpseclib\Math\BigInteger;
use Xrfg\Wallet\CollectERC\Traits\ETHHelper;

/**
 * @method string eth_blockNumber()
 * @method array  eth_getBlockByNumber(string $blockNumber, bool $true)
 * @method array  eth_getLogs(array $options)
 * @method        personal_sendTransaction(array $array, string $password)
 */
class ETHWallet
{
    use ETHHelper;

    // RPC 地址
    public $url;
    // 主钱包地址，从此钱包转出 ETH
    public $mainAddress;
    // 归集地址，转入 ERC到此钱包（可以和主钱包地址是同一地址）
    public $collectAddress;


    // ERC 合约地址
    public $contractAddress;
    // ERC 小数位数
    public $contractDecimals;
    // 转出界限，大于此值才会转出
    public $limit;

    // ETH 转账 gas limit
    public $ethGasLimit;
    // ETH 小数位数
    public $ethDecimals;
    // ERC 转账 gas limit
    public $ercGasLimit;
    // gas price
    public $gasPrice;


    /**
     * ETHService constructor.
     *
     * @param array $config 待归集币种配置
     */
    public function __construct($config)
    {
        $this->url = config('wallet.eth.node.wallet.base_uri');
        $this->mainAddress = $config['mainAddress'];
        $this->collectAddress = $config['collectAddress'];
        $this->contractAddress = $config['contractAddress'];
        $this->contractDecimals = $config['decimals'];
        $this->limit = (new BigInteger(10))->multiply(new BigInteger(pow(10, $this->contractDecimals), 10));
        $this->ethGasLimit = new BigInteger(21000);
        $this->ethDecimals = 18;
        $this->ercGasLimit = new BigInteger(60000);
        $this->gasPrice = new BigInteger(226 * pow(10, 9));
    }

    /**
     * RPC 请求.
     *
     * @param $url
     * @param $method
     * @param array $params
     * @return mixed
     * @throws Exception
     */
    function rpc($url, $method, array $params = [])
    {
        try {
            $response = (new Client)->post($url, [
                'json' => [
                    'id' => 1,
                    'jsonrpc' => '2.0',
                    'method' => $method,
                    'params' => $params,
                ],
            ]);
        } catch (Exception $exception) {
            logger('======ERC归集=======');
            Log::error($exception->getMessage());
            exit();
        }

        $response = json_decode($response->getBody()->getContents());
        if (isset($response->error)) {
            Log::error($response->error->code . ':' . $response->error->message);
            throw new Exception('RPC ERROR');
        }

        return $response->result;
    }

    /**
     * 获取钱包服务器地址列表
     * @return mixed
     */
    public function getAccounts()
    {
        return self::rpc($this->url, 'personal_listAccounts');
    }

    /**
     * 归集第一步：转入手续费.
     *
     * @param $address
     */
    function exportErcStep1($address)
    {
        $balance = self::rpc($this->url, 'eth_call', [
            [
                'data' => '0x70a08231000000000000000000000000' . substr($address, 2),
                'to' => $this->contractAddress,
            ],
            'latest',
        ]);
        $balance = new BigInteger($balance, 16);

        if ($balance->compare($this->limit) != -1) { // ERC数量不小于限定数量
            logger("{$address}有ERC要转出");
            $this->importFee($address, $this->mainAddress, $this->ethGasLimit, $this->ercGasLimit, $this->gasPrice);
        }

    }

    /**
     * 归集第二步：转出 ERC.
     *
     * @param $address
     */
    function exportErcStep2($address)
    {
        $balance = self::rpc($this->url, 'eth_call', [
            [
                'data' => '0x70a08231000000000000000000000000' . substr($address, 2),
                'to' => $this->contractAddress,
            ],
            'latest',
        ]);
        $balance = new BigInteger($balance, 16);

        if ($balance->compare($this->limit) != -1) { // ERC数量不小于限定数量
            try {
                $hash = self::rpc($this->url, 'personal_sendTransaction', [
                    [
                        'from' => $address,
                        'to' => $this->contractAddress,
                        'gas' => $this->num2hex($this->ercGasLimit),
                        'gasPrice' => $this->num2hex($this->gasPrice),
                        'data' => '0xa9059cbb000000000000000000000000' . substr($this->collectAddress, 2) . str_pad($balance->toHex(), 64, '0', STR_PAD_LEFT),
                    ],
                    config('wallet.eth.password')
                ]);
                echo $hash . PHP_EOL;
                logger("转出ERC的hash：{$hash}");
            } catch (Exception $exception) {
                logger("======{$address} 转出ERC失败=======");
                Log::error($exception);
            }

        }

    }

    /**
     * 向指定地址转入手续费
     * @param $address
     * @param $mainAddress
     * @param BigInteger $ethGasLimit
     * @param BigInteger $ercGasLimit
     * @param BigInteger $gasPrice
     */
    function importFee($address, $mainAddress, BigInteger $ethGasLimit, BigInteger $ercGasLimit, BigInteger $gasPrice)
    {
        $fee = $ercGasLimit->multiply($gasPrice);
        $balance = self::rpc($this->url, 'eth_getBalance', [$address, 'latest']);
        $balance = new BigInteger($balance, 16);
        if ($balance->compare($fee) == -1) { // 手续费不足
            try {
                $hash = self::rpc($this->url, 'personal_sendTransaction', [
                    [
                        'from' => $mainAddress,
                        'to' => $address,
                        'gas' => $this->num2hex($ethGasLimit),
                        'gasPrice' => $this->num2hex($gasPrice),
                        'value' => $this->num2hex($fee),
                    ],
                    config('wallet.eth.password')
                ]);
                echo $hash . PHP_EOL;
                logger("转入手续费hash：{$hash}");
            } catch (Exception $exception) {
                logger('======转入手续费失败=======');
                Log::error($exception);
            }
        }
    }

    /**
     * 销毁 ERC.
     * 转入地址：0x000000000000000000000000000000000000dead
     * @param $number 销毁数量
     */
    function destroyErc($number)
    {
        $balance = self::rpc($this->url, 'eth_call', [
            [
                'data' => '0x70a08231000000000000000000000000' . substr($this->mainAddress, 2),
                'to' => $this->contractAddress,
            ],
            'latest',
        ]);
        $balance = new BigInteger($balance, 16);

        $this->limit = (new BigInteger($number))->multiply(new BigInteger(pow(10, $this->contractDecimals), 10));
        if ($balance->compare($this->limit) != -1) { // ERC数量不小于销毁数量
            try {
                $hash = self::rpc($this->url, 'personal_sendTransaction', [
                    [
                        'from' => $this->mainAddress,
                        'to' => $this->contractAddress,
                        'gas' => $this->num2hex($this->ercGasLimit),
                        'gasPrice' => $this->num2hex($this->gasPrice),
                        'data' => '0xa9059cbb000000000000000000000000' . substr('0x000000000000000000000000000000000000dead', 2) . str_pad(substr($this->ether2wei($number, $this->contractDecimals), 2), 64, '0', STR_PAD_LEFT),
                    ],
                    config('wallet.eth.password')
                ]);
                echo $hash . PHP_EOL;
                logger("销毁ERC的hash：{$hash}");
            } catch (Exception $exception) {
                logger('======销毁ERC失败=======');
                Log::error($exception);
            }

        }

    }

    /**
     * ERC20-USDT转账.
     * @param $from 付款地址
     * @param $to 收款地址
     * @param $number 转账数量
     */
    function transferErc20Usdt($from, $to, $number)
    {
        $balance = self::rpc($this->url, 'eth_call', [
            [
                'data' => '0x70a08231000000000000000000000000' . substr($from, 2),
                'to' => $this->contractAddress,
            ],
            'latest',
        ]);
        $balance = new BigInteger($balance, 16);

        $this->limit = (new BigInteger($number))->multiply(new BigInteger(pow(10, $this->contractDecimals), 10));
        if ($balance->compare($this->limit) != -1) { // ERC数量不小于转账数量
            try {
                $hash = self::rpc($this->url, 'personal_sendTransaction', [
                    [
                        'from' => $from,
                        'to' => $this->contractAddress,
                        'gas' => $this->num2hex($this->ercGasLimit),
                        'gasPrice' => $this->num2hex($this->gasPrice),
                        'data' => '0xa9059cbb000000000000000000000000' . substr($to, 2) . str_pad(substr($this->ether2wei($number, $this->contractDecimals), 2), 64, '0', STR_PAD_LEFT),
                    ],
                    config('wallet.eth.password')
                ]);
                echo $hash . PHP_EOL;
                logger("ERC-USDT转账hash：{$hash}");
            } catch (Exception $exception) {
                logger('======ERC-USDT转账失败=======');
                Log::error($exception);
            }

        }

    }
}
